/*
 * TcpListener.cpp
 *
 *  Created on: Jun 22, 2016
 *      Author: Lucifer
 */

#include "./TcpListener.h"

namespace BFX {

namespace IMPL {
/// Accept only channel uses for BFX::TcpListener
struct TcpListenerChannel : public SelectableChannel {
	TcpListenerChannel(Socket* socket, TcpListener* listener) :
		SelectableChannel(socket), _listener(listener) {
		BFX_ASSERT(listener);
	}
	virtual ~TcpListenerChannel() {
		close();
	}
	/// @copydoc SelectableChannel::getValidOps()
	virtual int getValidOps() const {
		return SelectionKey::OP_ACCEPT;
	}
	/// @copydoc SelectableChannel::setReadyOps()
	virtual bool setReadyOps(int ops, SelectionKey* key) {
		BFX_ASSERT(key);
		int oldOps = key->getReadyOps();
		int newOps = (ops & key->getInterestOps());

		key->setReadyOps(newOps);
		return !!(newOps & ~oldOps);
	}
	/// @copydoc SelectableChannel::closeImpl()
	virtual void closeImpl() {
		_listener = nullptr;
		SelectableChannel::closeImpl();
	}
	TcpListener* _listener;
};
}	// namespace IMPL

TcpListener::TcpListener(InetAddress* address) :
		_active(false), _callback(nullptr) {
	REF<Socket> ls = new Socket(address);
	ls->setReusePort(true);	// will not work on several platforms.
	_channel = new IMPL::TcpListenerChannel(ls, this);
}
TcpListener::~TcpListener() {
	stop();
}

bool TcpListener::start(Selector* sel, ListenerCallback callback) {
	//already listening
	if (_active) {
		return false;	// already started.
	}
	_active = _channel->getSocket()->listen(SOMAXCONN);
	if (_active) {
		_callback = callback;
		_channel->registerKey(sel, SelectionKey::OP_ACCEPT, eventCallback);
	}

	return _active;
}
void TcpListener::stop() {
	if (!_active)
		return;
	_active = false;
	_channel->close();
}

bool TcpListener::accept(Socket* rSocket) {
	return _channel->getSocket()->accept(rSocket);
}

void TcpListener::eventCallback(SelectionKey* key) {
	BFX_ASSERT(key);
	IMPL::TcpListenerChannel* channel =
			reinterpret_cast<IMPL::TcpListenerChannel*>(key->getChannel());

	// TODO except events

	// Notify the caller with the source listener.
	TcpListener* listener = channel->_listener;
	BFX_ASSERT(listener && listener->_active);
	TcpListener::ListenerCallback callback = listener->_callback;
	callback(listener);
}

} /* namespace BFX */
